// Copyright Epic Games, Inc. All Rights Reserved.

#include "Player/LyraLocalPlayer.h"

#include "AudioMixerBlueprintLibrary.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "Settings/LyraSettingsLocal.h"
#include "Settings/LyraSettingsShared.h"
#include "CommonUserSubsystem.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraLocalPlayer) // 包含由UE生成的代码

class UObject;

/**
 * 构造函数。
 */
ULyraLocalPlayer::ULyraLocalPlayer()
{
}

/**
 * 初始化属性后调用。
 */
void ULyraLocalPlayer::PostInitProperties()
{
	Super::PostInitProperties(); // 调用父类方法

	if (ULyraSettingsLocal* LocalSettings = GetLocalSettings()) // 获取本地设置
	{
		LocalSettings->OnAudioOutputDeviceChanged.AddUObject(this, &ULyraLocalPlayer::OnAudioOutputDeviceChanged); // 绑定音频输出设备改变事件
	}
}

/**
 * 切换控制器。
 */
void ULyraLocalPlayer::SwitchController(class APlayerController* PC)
{
	Super::SwitchController(PC); // 调用父类方法

	OnPlayerControllerChanged(PlayerController); // 调用玩家控制器改变回调
}

/**
 * 生成玩家角色。
 */
bool ULyraLocalPlayer::SpawnPlayActor(const FString& URL, FString& OutError, UWorld* InWorld)
{
	const bool bResult = Super::SpawnPlayActor(URL, OutError, InWorld); // 调用父类方法

	OnPlayerControllerChanged(PlayerController); // 调用玩家控制器改变回调

	return bResult; // 返回结果
}

/**
 * 初始化在线会话。
 */
void ULyraLocalPlayer::InitOnlineSession()
{
	OnPlayerControllerChanged(PlayerController); // 调用玩家控制器改变回调

	Super::InitOnlineSession(); // 调用父类方法
}

/**
 * 玩家控制器改变回调。
 */
void ULyraLocalPlayer::OnPlayerControllerChanged(APlayerController* NewController)
{
	// 停止监听旧控制器的改变
	FGenericTeamId OldTeamID = FGenericTeamId::NoTeam; // 旧队伍ID
	if (ILyraTeamAgentInterface* ControllerAsTeamProvider = Cast<ILyraTeamAgentInterface>(LastBoundPC.Get())) // 获取队伍代理接口
	{
		OldTeamID = ControllerAsTeamProvider->GetGenericTeamId(); // 获取旧队伍ID
		ControllerAsTeamProvider->GetTeamChangedDelegateChecked().RemoveAll(this); // 移除所有绑定
	}

	// 获取当前队伍ID并监听未来改变
	FGenericTeamId NewTeamID = FGenericTeamId::NoTeam; // 新队伍ID
	if (ILyraTeamAgentInterface* ControllerAsTeamProvider = Cast<ILyraTeamAgentInterface>(NewController)) // 获取队伍代理接口
	{
		NewTeamID = ControllerAsTeamProvider->GetGenericTeamId(); // 获取新队伍ID
		ControllerAsTeamProvider->GetTeamChangedDelegateChecked().AddDynamic(this, &ThisClass::OnControllerChangedTeam); // 绑定队伍改变事件
		LastBoundPC = NewController; // 更新最后绑定的控制器
	}

	ConditionalBroadcastTeamChanged(this, OldTeamID, NewTeamID); // 条件广播队伍改变
}

/**
 * 设置通用队伍ID。
 */
void ULyraLocalPlayer::SetGenericTeamId(const FGenericTeamId& NewTeamID)
{
	// 什么都不做，我们仅观察关联玩家控制器的队伍
}

/**
 * 获取通用队伍ID。
 */
FGenericTeamId ULyraLocalPlayer::GetGenericTeamId() const
{
	if (ILyraTeamAgentInterface* ControllerAsTeamProvider = Cast<ILyraTeamAgentInterface>(PlayerController)) // 获取队伍代理接口
	{
		return ControllerAsTeamProvider->GetGenericTeamId(); // 返回队伍ID
	}
	else
	{
		return FGenericTeamId::NoTeam; // 返回无队伍
	}
}

/**
 * 获取队伍索引改变委托。
 */
FOnLyraTeamIndexChangedDelegate* ULyraLocalPlayer::GetOnTeamIndexChangedDelegate()
{
	return &OnTeamChangedDelegate; // 返回队伍改变委托
}

/**
 * 获取本地设置。
 */
ULyraSettingsLocal* ULyraLocalPlayer::GetLocalSettings() const
{
	return ULyraSettingsLocal::Get(); // 返回本地设置
}

/**
 * 获取共享设置。
 */
ULyraSettingsShared* ULyraLocalPlayer::GetSharedSettings() const
{
	if (!SharedSettings) // 如果共享设置不存在
	{
		// 在PC上可以使用同步加载，因为它只检查磁盘
		// 这可以使用平台标签来检查适当的保存支持
		bool bCanLoadBeforeLogin = PLATFORM_DESKTOP; // 桌面平台可以登录前加载
		
		if (bCanLoadBeforeLogin) // 如果可以登录前加载
		{
			SharedSettings = ULyraSettingsShared::LoadOrCreateSettings(this); // 加载或创建设置
		}
		else // 如果不能登录前加载
		{
			// 需要等待用户登录获取真实设置，因此返回临时设置
			SharedSettings = ULyraSettingsShared::CreateTemporarySettings(this); // 创建临时设置
		}
	}

	return SharedSettings; // 返回共享设置
}

/**
 * 从磁盘加载共享设置。
 */
void ULyraLocalPlayer::LoadSharedSettingsFromDisk(bool bForceLoad)
{
	FUniqueNetIdRepl CurrentNetId = GetCachedUniqueNetId(); // 获取缓存的唯一网络ID
	if (!bForceLoad && SharedSettings && CurrentNetId == NetIdForSharedSettings) // 如果不强制加载且设置已加载且网络ID相同
	{
		// 已加载过一次，不再重新加载
		return;
	}

	ensure(ULyraSettingsShared::AsyncLoadOrCreateSettings(this, ULyraSettingsShared::FOnSettingsLoadedEvent::CreateUObject(this, &ULyraLocalPlayer::OnSharedSettingsLoaded))); // 异步加载或创建设置
}

/**
 * 共享设置加载完成回调。
 */
void ULyraLocalPlayer::OnSharedSettingsLoaded(ULyraSettingsShared* LoadedOrCreatedSettings)
{
	// 在到达这里之前设置已被应用
	if (ensure(LoadedOrCreatedSettings)) // 确保设置有效
	{
		// 这将替换临时或先前加载的对象，该对象将正常被GC回收
		SharedSettings = LoadedOrCreatedSettings; // 更新共享设置

		NetIdForSharedSettings = GetCachedUniqueNetId(); // 更新网络ID
	}
}

/**
 * 音频输出设备改变回调。
 */
void ULyraLocalPlayer::OnAudioOutputDeviceChanged(const FString& InAudioOutputDeviceId)
{
	FOnCompletedDeviceSwap DevicesSwappedCallback; // 设备交换完成回调
	DevicesSwappedCallback.BindUFunction(this, FName("OnCompletedAudioDeviceSwap")); // 绑定回调函数
	UAudioMixerBlueprintLibrary::SwapAudioOutputDevice(GetWorld(), InAudioOutputDeviceId, DevicesSwappedCallback); // 交换音频输出设备
}

/**
 * 音频设备交换完成回调。
 */
void ULyraLocalPlayer::OnCompletedAudioDeviceSwap(const FSwapAudioOutputResult& SwapResult)
{
	if (SwapResult.Result == ESwapAudioOutputDeviceResultState::Failure) // 如果交换失败
	{
		// 处理失败情况
	}
}

/**
 * 控制器队伍改变回调。
 */
void ULyraLocalPlayer::OnControllerChangedTeam(UObject* TeamAgent, int32 OldTeam, int32 NewTeam)
{
	ConditionalBroadcastTeamChanged(this, IntegerToGenericTeamId(OldTeam), IntegerToGenericTeamId(NewTeam)); // 条件广播队伍改变
}